package com.wk.springframework.beans.factory.support;

import cn.hutool.core.lang.Assert;
import com.wk.springframework.beans.factory.DisposableBean;
import com.wk.springframework.beans.factory.ObjectFactory;
import com.wk.springframework.beans.factory.config.SingletonBeanRegistry;
import com.wk.springframework.common.exception.BeanException;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    private final Map<String, Object> singletonObjects = new HashMap<>();

    // 二级缓存，提前暴漏对象，没有完全实例化的对象
    protected final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();

    // 三级缓存，存放代理对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>();


    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    /**
     * Internal marker for a null singleton object:
     * used as marker value for concurrent Maps (which don't support null values).
     */
    protected static final Object NULL_OBJECT = new Object();

    @Override
    public Object getSingleton(String beanName) {
        Object bean = singletonObjects.get(beanName);
        if (Objects.nonNull(bean)) return bean;
        bean = earlySingletonObjects.get(beanName);
        if (Objects.nonNull(bean)) return bean;
        ObjectFactory<?> objectFactory = singletonFactories.get(beanName);
        if (Objects.nonNull(objectFactory)){
            bean = objectFactory.getObject();
            earlySingletonObjects.put(beanName,bean);
            singletonFactories.remove(beanName);
            return bean;
        }
        return bean;
    }


    protected void addSingleton(String beanName, Object singletonObject) {
        this.singletonObjects.put(beanName, singletonObject);
    }



    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }


    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    @Override
    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                e.printStackTrace();
                throw new BeanException("Destroy method on bean with name '" + beanName + "' threw an exception");
            }
        }
    }

    public void registerSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            singletonObjects.put(beanName, singletonObject);
            earlySingletonObjects.remove(beanName);
            singletonFactories.remove(beanName);
        }
    }
}
